/**
 * Advanced Software Engineering (Monica Farrow)
 * DepotUtils class
 * 
 * Some of the methods in this class have been based on lecture notes provided by
 * Monica Farrow as a part of Software Engineering courses at Heriot-Watt University.
 */

package depotAdmin;


import java.text.DecimalFormat;
import java.util.Random;

import depot.*;

/**
 * DepotUtils class for generating random customer and parcel objects
 * and for storing and manipulating variables used throughout the program
 * @author Ionut Iordache, Nikky Afunugo, Dave Bond
 */
public class DepotUtils {

	private static String[] pclIDprefix = {"X","S","E"}; // prefixes for parcelID
	// string array of firstNames
	private static String[] fnames = {"Octavian","Dave","Nikky","Billy","Fabio","Monica","Richard","Kirsty","Marion","Gaylord"};	
	// String array of lastNames
	private static String[] lnames = {"Iordache","Bond","Afunugo","Farrow","Flibberty","Toffee","Quartermaine","Bottomley","McFarlane","O'Shea"};	

	// static variables
	// indicates end of working day
	private static Boolean endOfDay = false; 

	// indicates when threads have finished their tasks
	private static Boolean pclLoopFinished = false; 
	private static Boolean custLoopFinished = false; 
	private static Boolean procLoopFinished = false; 

	private static int speedLevel = 2; // holds current speed level

	// speed values to set speed variables
	private static long[] initAndTidyUpSpeedValues = {500,250,125,62,31};
	private static long[] parcelAddSpeedValues = {3333,1666,833,417,208};
	private static long[] custAddSpeedValues = {4444,2222,1111,555,278};
	private static long[] procSpeedValues = {4444,2222,1111,555,278};

	// variables to store data used in final report
	private static int totParcels = 0; // total number of parcels (collected & uncollected)
	private static double totWeight = 0; // total weight of all parcels
	private static int totDays = 0; // total days in depot of all parcels


	/** 
	 * returns a random int between the specified parameters
	 * used in generation of parcel and customer objects
	 * @param aStart  the lowest possible int
	 * @param aEnd  the highest possible int
	 * @return random int
	 */
	public static int getRandomInteger(int aStart, int aEnd){
		Random random = new Random();		    
		//get the range, casting to long to avoid overflow problems
		long range = (long)aEnd - (long)aStart + 1;
		// compute a fraction of the range, 0 <= frac < range
		long fraction = (long)(range * random.nextDouble());
		int randomNumber =  (int)(fraction + aStart);  
		return randomNumber;
	}


	/** 
	 * returns a random parcelID
	 * used in generation of parcel objects
	 * @return the random parcelID
	 */
	private static String getPclID(){

		// get parcel prefix
		String pclID = pclIDprefix[getRandomInteger(0, 2)];

		// get rest of parcelID (all digits)
		for (int i = 0; i < 6; i++) {
			pclID += getRandomInteger(0, 9);
		}

		return pclID;
	} 


	/** 
	 * returns random parcel dimensions
	 * used in generation of parcel objects
	 * @return random parcel dimensions
	 */
	private static int[] getPclDims(){
		int[] pclDims = new int[3];

		// generate random dimensions (all random integers)
		for (int i = 0; i < 3; i++) {
			pclDims[i] = getRandomInteger(1, 61);
		}

		return pclDims;
	} 


	/** 
	 * returns a random Double between the specified parameters
	 * used in generation of parcel objects
	 * and for random variation to speed levels
	 * @param aStart  the lowest possible Double
	 * @param aEnd  the highest possible Double
	 * @return random Double
	 */
	public static Double getRandomDouble(Double aStart, Double aEnd){
		// set format for weight
		DecimalFormat df = new DecimalFormat("###.##");	
		// create value
		double myDouble = (double) (Math.random() * (aEnd - aStart) + aStart);
		// apply format
		String doubleAsString = df.format(myDouble);
		// convert back to Double
		myDouble = Double.valueOf(doubleAsString);
		return myDouble;
	}


	/** 
	 * returns a random customerID
	 * used in generation of customer objects
	 * @return the random customerID
	 */
	private static String getCustID(){
		String custID = "";

		// generate 6 random digits
		for (int i = 0; i < 6; i++) {
			custID += getRandomInteger(0, 9);
		}

		return custID;
	} 


	/** 
	 * returns a random firstName
	 * used in generation of customer objects
	 * @return the random firstName
	 */
	private static String getRandFname(){
		// chooses a random first name from the ArrayList
		String custFname = fnames[getRandomInteger(0, 9)];
		return custFname;
	} 


	/** 
	 * returns a random lastName
	 * used in generation of customer objects
	 * @return the random lastName
	 */
	private static String getRandLname(){
		// chooses a random last name from the ArrayList
		String custLname = lnames[getRandomInteger(0, 9)];
		return custLname;
	} 


	/** 
	 * returns a randomly generated parcel object
	 * @return the random parcel object
	 */
	public static Parcel getRandPcl() throws IllegalArgumentException {
		// randomly generate parcel elements
		String id = getPclID();
		int[] dims = getPclDims();
		double weight = getRandomDouble(0.01, 10.01);
		// always set days to 1 (i.e. new to the depot)
		int days = 1;

		Parcel pcl = new Parcel(id, dims, weight, days);
		return pcl;
	} 


	/** 
	 * returns a randomly generated customer object
	 * @param pcl  Parcel object needed for customer
	 * @return the random customer object
	 */
	public static Customer getRandCust(Parcel pcl) throws IllegalArgumentException {
		// randomly generate customer elements
		String id = getCustID();
		String fname = getRandFname();
		String lname = getRandLname();

		Customer cust = new Customer(id, fname, lname, pcl);
		return cust;

	} 


	/**
	 * gets value of endOfDay variable
	 * @return Boolean endOfDay 
	 */
	public static Boolean getEndOfDay() {
		return endOfDay;
	}


	/**
	 * sets value of endOfDay variable to that contained in parameter
	 * @param endOfDay  Boolean object
	 */
	public static void setEndOfDay(Boolean endOfDay) {
		DepotUtils.endOfDay = endOfDay;
	}


	/**
	 * gets value of initAndTidyUpSpeed
	 * @return long initAndTidyUpSpeed 
	 */
	public static long getInitAndTidyUpSpeed() {
		return initAndTidyUpSpeedValues[speedLevel-1];
	}


	/**
	 * gets value of parcelAddSpeed
	 * @return long parcelAddSpeed 
	 */
	public static long getParcelAddSpeed() {
		return parcelAddSpeedValues[speedLevel-1];
	}


	/**
	 * gets value of custAddSpeed
	 * @return long custAddSpeed 
	 */
	public static long getCustAddSpeed() {
		return custAddSpeedValues[speedLevel-1];
	}


	/**
	 * gets value of procSpeed
	 * @return long procSpeed 
	 */
	public static long getProcSpeed() {
		return procSpeedValues[speedLevel-1];
	}


	/**
	 * gets value of speedLevel
	 * @return int speedLevel 
	 */
	public static int getSpeedLevel() {
		return speedLevel;
	}


	/**
	 * increments value of speedLevel by 1
	 * and thus sets separate processing speeds to appropriate values
	 */
	public static void setSpeedLevelUp() {
		if(getSpeedLevel() < 5) { // can't get higher than 5
			DepotUtils.speedLevel++;
		}
	}


	/**
	 * decrements value of speedLevel by 1
	 * and thus sets separate processing speeds to appropriate values
	 */
	public static void setSpeedLevelDown() {
		if(getSpeedLevel() > 1) { // can't get lower than 1
			DepotUtils.speedLevel--;
		}
	}


	/**
	 * gets value of pclLoopFinished variable
	 * @return Boolean pclLoopFinished 
	 */
	public static Boolean getPclLoopFinished() {
		return pclLoopFinished;
	}


	/**
	 * sets value of pclLoopFinished variable to that contained in parameter
	 * @param plf  Boolean object
	 */
	public static void setPclLoopFinished(Boolean plf) {
		DepotUtils.pclLoopFinished = plf;
	}


	/**
	 * gets value of custLoopFinished variable
	 * @return Boolean custLoopFinished 
	 */
	public static Boolean getCustLoopFinished() {
		return custLoopFinished;
	}


	/**
	 * sets value of custLoopFinished variable to that contained in parameter
	 * @param clf   Boolean object
	 */
	public static void setCustLoopFinished(Boolean clf) {
		DepotUtils.custLoopFinished = clf;
	}


	/**
	 * gets value of procLoopFinished variable
	 * @return Boolean procLoopFinished 
	 */
	public static Boolean getProcLoopFinished() {
		return procLoopFinished;
	}


	/**
	 * sets value of procLoopFinished variable to that contained in parameter
	 * @param prlf    Boolean object
	 */
	public static void setProcLoopFinished(Boolean prlf) {
		DepotUtils.procLoopFinished = prlf;
	}


	/**
	 * gets value of totParcels
	 * @return int totParcels 
	 */
	public static int getTotParcels() {
		return totParcels;
	}


	/**
	 * increments value of totParcels
	 */
	public static void incrementTotParcels() {
		totParcels++;
	}


	/**
	 * decrements value of totParcels
	 */
	public static void decrementTotParcels() {
		totParcels--;
	}


	/**
	 * gets value of totWeight
	 * @return double totWeight 
	 */
	public static double getTotWeight() {
		return totWeight;
	}


	/**
	 * increments value of totWeight by the value contained in the parameter
	 * @param wgt  double value
	 */
	public static void incrementTotWeight(Double wgt) {
		totWeight = totWeight + wgt;
	}


	/**
	 * decrements value of totWeight by the value contained in the parameter
	 * @param wgt  double value
	 */
	public static void decrementTotWeight(Double wgt) {
		totWeight = totWeight - wgt;
	}


	/**
	 * gets value of totDays
	 * @return int totDays 
	 */
	public static int getTotDays() {
		return totDays;
	}


	/**
	 * increments value of totDays by the value contained in the parameter
	 * @param days    int value
	 */
	public static void incrementTotDays(int days) {
		totDays = totDays + days;
	}


	/**
	 * decrements value of totDays by the value contained in the parameter
	 * @param days    int value
	 */
	public static void decrementTotDays(int days) {
		totDays = totDays - days;
	}


}
